<?php
/**
 * Created by 784855684@qq.com.
 * Link: https://github.com/lichtung/inframe
 * User: linzh
 * Date: 6/25/17
 * Time: 5:36 PM
 */



namespace inframe\core\view;

use inframe\core\Cache;
use inframe\core\Storage;
use inframe\helper\Backtrace;
use inframe\helper\XMLer;
use inframe\throws\FileNotFoundException;

/**
 *
 * trait Render 内置渲染引擎
 *
 * 功能：
 *  ①模板编译部分实现了“布局替换”和“组件编译”两项
 *  ②display方法实现了模板缓存的控制
 *
 * @package inframe\core\view
 */
trait Render
{
    /**
     * @var array 模板常量
     */
    private $_template_contants = [
        '__PUBLIC__' => IN_PUBLIC_URL,
        '__CDN__' => IN_CDN,
    ];

    /**
     * @var array 待分配到模板中的变量
     */
    private $_template_vars = [];

    /**
     * 分配模板变量
     * @param string|array $name
     * @param mixed|null $value
     * @return $this
     */
    protected function assign($name, $value = null)
    {
        if (is_array($name)) {
            foreach ($name as $_key => $_val) {
                $_key and $this->_template_vars[$_key] = $_val;
            }
        } else {
            $name and $this->_template_vars[$name] = $value;
        }
        return $this;
    }

    /**
     * 解析布局
     * 注： 模具文件中还可以布局
     * @param string $content 模板内容
     * @param string $module 模块名称
     * @return void
     */
    private function _naz_parseLayout( &$content, $module)
    {
        if (strpos($content, '<layout:') !== false) {
            if (preg_match('/<layout:([\w\d_\/]+)(\s*)(\/)?>/', $content, $match) and !empty($match[1])) {
                if (strpos($match[1], '/') === 0) {
                    $layout_file = IN_PATH_APP . trim($match[1], '/') . '.php';
                } else {
                    $layout_file = IN_PATH_BASE . $module . "/view/layout/" . trim($match[1], '/') . ".php";
                }
                if (is_file($layout_file)) {
                    $layout_content = file_get_contents($layout_file);
                    $this->_naz_parseLayout($layout_content, $module);
                    $content = str_replace($match[0], '', $content);
                    $content = str_replace('<layout-content/>', $content, $layout_content);
                }
            }
        }
    }

    private function _naz_parseComponent( &$content)
    {
        if (strpos($content, '<component:') !== false) {
            if (preg_match_all('/<component:([^\s]+)((\s*\/>)|(([^\/]*)\/>)|(([^>]+)?>([^`]+)?<\/component:\1>))/', $content, $matches)) {
                $search = [];
                $replace = [];
                # 0 - 全部
                # 1 - component类名称
                # 2 - X
                # 3 - X
                # 4 - X
                # 5 - 单标签属性列表
                # 6 - X
                # 7 - 双标签属性列表
                # 8 - 刷标签内部元素
                $len = count($matches[0]);
                for ($i = 0; $i < $len; $i++) {
                    # 组建名称使用绝对的明明空间
                    $component = '\\' . str_replace('.', '\\', $matches[1][$i]);
                    if (empty($matches[5][$i])) {
                        $attrs = trim($matches[7][$i]) ? XMLer::parseAttrs($matches[7][$i]) : [];
                    } else {
                        $attrs = trim($matches[2][$i]) ? XMLer::parseAttrs($matches[2][$i]) : [];
                    }

                    $set = '';
                    $instance = '$component_' . $i;
                    $innerhtml = isset($matches[8][$i])?$matches[8][$i]:'';
                    # 双标签内部之间成为innerHTML属性
                    $set .= "{$instance}->innerHTML = '{$innerhtml}';";
                    if ($attrs) foreach ($attrs as $attrname => $varname) {
                        # 如果属性值以'$'开头，则表示自动从运行环境中获取这个变量
                        if (strpos($varname, '$') === 0) {
                            $set .= "{$instance}->{$attrname} = isset({$varname})?{$varname}:null; ";
                        } else {
                            $set .= "{$instance}->{$attrname} = '{$varname}'; ";
                        }
                    }

                    $search[] = $matches[0][$i];
                    $replace[] = "<?php {$instance} =  new {$component}(); {$set}; echo {$instance}; ?>";
                }
                $content = str_replace($search, $replace, $content);
            }
        }
    }

    /**
     * 显示视图
     * @param string $template 模板文件名称，默认使用调用方法的名称
     * @param int $expire 缓存过期时间，如果是0表示不缓存
     * @return void
     * @throws FileNotFoundException
     */
    final protected function display($template = '',$expire = 0)
    {
        $cache = null;
        if ('' === $template) {
            # 获取 调用方法的名称
            $template = Backtrace::getPreviousMethod();
        }
        $classname = static::class;
        $pos = strpos($classname, '\\controller\\');
        $controller = substr($classname, $pos + 12);
        $module = str_replace('\\', '/', trim(substr($classname, 0, $pos), '\\'));

        $key = md5("/{$module}/{$controller}/{$template}");
        if ($expire) {
            $cache = Cache::getInstance();
            if ($content = $cache->get($key, null)) {
                echo $content;
                return;
            }
        }

        # 检查编译后的模板
        $view = IN_PATH_BASE . $module . "/view/{$controller}/{$template}.php";
        $compile_view = IN_PATH_RUNTIME . 'view/' . "{$controller}/{$template}.php";;
        if (IN_DEBUG_MODE_ON or !is_file($compile_view) or (filemtime($view) > filemtime($compile_view))) {
            # 编译模板不存在或者修改时间滞后
            if (is_file($view)) {
                $content = file_get_contents($view);

                # 布局解析
                $this->_naz_parseLayout($content, $module);

                # 组件解析
                $this->_naz_parseComponent($content);

                Storage::write($compile_view, $content);
            } else {
                throw new FileNotFoundException($compile_view);
            }
        }


        # 模板常量和变量替换
        $vars = [];
        $search = array_keys($this->_template_contants);
        $replace = array_values($this->_template_contants);

        if ($this->_template_vars) foreach ($this->_template_vars as $name => $value) {
            if (is_array($value) or is_object($value) or is_resource($value)) {
                # 复杂类型不替换模板变量
            } else {
                # bool -> int
                if (is_bool($value)) {
                    $value = $value ? 1 : 0;
                }
                # int or string or float
                $search[] = "{{{$name}}}";
                $replace[] = $value;
            }
            $vars[$name] = $value;
        }

        # 加载编译后的文件并替换变量
        ob_start();
        \inframe::template($compile_view, $vars, false, true);
        $content = str_replace($search, $replace, ob_get_clean());

        if ($expire) {
            $cache or $cache = Cache::getInstance();
            $cache->set($key, $content, $expire);
        }

        echo $content;
    }


}